﻿using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Babylon.Toolbox.ModelBuilding
{
    public abstract class MeshContent
    {
        readonly ModelContent _parent;
        readonly bool _optimize;
        readonly bool _generatePickingInformations;
        readonly VertexDeclaration _vertexDeclaration;
        readonly List<MeshPartContent> _parts = new List<MeshPartContent>();

        readonly Dictionary<string, int> _registeredVertices = new Dictionary<string, int>();
        readonly List<int> _indicesHashTable = new List<int>();
         List<int> _stagingIndices = new List<int>();

        protected List<int> StagingIndices { get { return _stagingIndices; } }
        protected List<int> IndicesHashTable { get { return _indicesHashTable; } }
        protected Dictionary<string, int> RegisteredVertices { get { return _registeredVertices; } }
        public int VerticesCount { get; protected set; }

        public List<MeshPartContent> Parts
        {
            get { return _parts; }
        }

        public List<int > Indices { get; private set; }

        public VertexDeclaration VertexDeclaration
        {
            get { return _vertexDeclaration; }
        }

        public int FaceCount { get; private set; }

        public bool Optimize { get { return _optimize; } }

        public bool GeneratePickingInformations
        {
            get { return _generatePickingInformations; }
        }

        protected MeshContent(ModelContent parent, bool optimize, bool generatePickingInformations, VertexDeclaration vertexDeclaration)
        {
            _generatePickingInformations = generatePickingInformations;
            _vertexDeclaration = vertexDeclaration;
            _parent = parent;
            _optimize = optimize;
            _parent.AddChild(this);
        }

        public void AddIndex(int index)
        {
            StagingIndices.Add(index);
        }

        protected void ProcessParts(){
            var groups = _parts.GroupBy(p => p.Material).ToList();
            _parts.Clear();
            Indices = new List<int>();

            foreach (var group in groups)
            {
                MeshPartContent meshPart = new MeshPartContent(this) { Material = group.Key, StartIndex = Indices.Count, MinVertexIndex = int.MaxValue };

                foreach (MeshPartContent child in group)
                {
                    for (int index = 0; index < child.PrimitivesCount * 3; index++)
                    {
                        Indices.Add(StagingIndices[child.StartIndex + index]);
                    }

                    meshPart.MinVertexIndex = Math.Min(meshPart.MinVertexIndex, child.MinVertexIndex);
                    meshPart.NumVertices += child.NumVertices;
                    meshPart.PrimitivesCount += child.PrimitivesCount;
                }

            }

            StagingIndices.Clear();
            _stagingIndices = null;

            FaceCount = Indices.Count / 3;
        }

        internal abstract int AppendToVertexBuffer(VertexBuffer vertexBuffer, int vertexBufferOffset, out BoundingSphere boundingSphere, out Vector3[] positions);

        internal int AppendToIndexBuffer(IndexBuffer indexBuffer, int indexBufferOffset, out int[] indices)
        {
            indices = Indices.ToArray();
            indexBuffer.SetData(indexBufferOffset, Indices.ToUShortArray(), 0, Indices.Count);

            return indexBufferOffset + Indices.Count * 2;
        }

        public void BuildMeshParts(ModelMesh modelMesh, GraphicsDevice device, IMaterialConverter materialConverter)
        {
            foreach (var meshPartContent in Parts)
            {
                new ModelMeshPart(modelMesh)
                               {
                                   Effect = materialConverter.Convert(device, meshPartContent.Material),
                                   MinVertexIndex = meshPartContent.MinVertexIndex,
                                   NumVertices = meshPartContent.NumVertices,
                                   PrimitivesCount = meshPartContent.PrimitivesCount,
                                   StartIndex = meshPartContent.StartIndex
                               };
            }
        }
    }
}
